<#
Copyright 2012 Chris Duck

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
#>

function HasDependencyBeenChecked {
	param(
		$Dependency,
		$DependencyList
	)
	$IsMissing = $false
	$DependencyList.Missing | %{
		if($_.Id -eq $Dependency.Id) {
			#If any version of this dependency is already missing, don't check again
			$IsMissing = $true
		}
	}
	$IsNeeded = $false
	$DependencyList.Needed | ?{$_.Id -eq $Dependency.Id } | %{
		if($_.ServerVersion -ge $Dependency.Version) {
			#If the version in the list is greater than the required version, don't check again
			$IsNeeded = $true
		}
	}
	
	$IsInstalled = $false
	$DependencyList.Installed | ?{$_.Id -eq $Dependency.Id } | %{
		if($_.ServerVersion -ge $Dependency.Version) {
			#If the version in the list is greater than the required version, don't check again
			$IsInstalled = $true
		}
	}
	return $IsMissing -or $IsNeeded -or $IsInstalled
}

function BuildDependencyList {
	param(
		[Parameter(Mandatory=$true)]
		[ValidateNotNullOrEmpty()]
		$RootPackage,
		[Parameter(Mandatory=$false)]
		$DependencyList
	)
	
	if(!$DependencyList) {
		$DependencyList = "" | Select Installed,Needed,Missing
		$DependencyList.Installed = New-Object Collections.ArrayList
		$DependencyList.Needed = New-Object Collections.ArrayList
		$DependencyList.Missing = New-Object Collections.ArrayList
	}
	
	if($RootPackage.DependencyList.Count -gt 0) {
		$RootPackage.DependencyList | %{
			$Dependency = $_
			if( !(HasDependencyBeenChecked -Dependency $_ -DependencyList $DependencyList) ) {
				$SourceName = $RootPackage.Source.Name
				$PackageOnServer = Get-NuGetPackage -Source $SourceName -Id $Dependency.Id
				$PackageBucket = ""
				if($PackageOnServer) {
					if([String]::IsNullOrEmpty($Dependency.Version)) {
						#There is no minimum version specified for this dependency and it exists on the server.
						if($PackageOnServer.InstalledVersion) {
							#Package exists and is installed
							Write-Debug "BuildDependencyList: $($Dependency.Id) is already installed."
							$PackageBucket = "Installed"
						} else {
							#Package exists but needs to be installed
							Write-Debug "BuildDependencyList: $($Dependency.Id) exists, but needs to be installed."
							$PackageBucket = "Needed"
						}
					} else {
						#There is a minimum version specified and some version of the package exists on the server
						if($PackageOnServer.InstalledVersion -ge $Dependency.Version) {
							#the package is already installed
							Write-Debug "BuildDependencyList: $($Dependency.Id) >= $($Dependency.Version) is already installed."
							$PackageBucket = "Installed"
						} else {
							if($PackageOnServer.ServerVersion -ge $Dependency.Version) {
								#The package is not installed, but is available on the server
								Write-Debug "BuildDependencyList: $($Dependency.Id) >= $($Dependency.Version) exists, but needs to be installed."
								$PackageBucket = "Needed"
							} else {
								#the version on the server is not high enough to satisfy the dependency
								Write-Debug "BuildDependencyList: $($Dependency.Id) >= $($Dependency.Version) is not found on source '$SourceName'."
								$PackageBucket = "Missing"
							}
						}
					}
				} else {
					#Package was not on server at all
					Write-Debug "BuildDependencyList: $($Dependency.Id) does not exist on source '$SourceName'."
					$PackageBucket = "Missing"
				}
				switch ($PackageBucket) {
					"Installed" {
						$DependencyList.Installed | ?{$_.Id -eq $Dependency.Id} | %{
							#remove any previously found instances of this dependency.  These should be for lower versions.
							$DependencyList.Installed.Remove($_)
						}
						$DependencyList.Installed.Add($Dependency) > $null
						#Since it's installed, assume it's dependencies are already resolved
					}
					"Missing" {
						$DependencyList.Missing | ?{$_.Id -eq $Dependency.Id} | %{
							#remove any previously found instances of this dependency.  These should be for lower versions.
							$DependencyList.Missing.Remove($_)
						}
						$DependencyList.Missing.Add($Dependency) > $null
						#Since it's missing, we can't verify its dependencies
					}
					"Needed" {
						$DependencyList.Needed | ?{$_.Id -eq $Dependency.Id} | %{
							#remove any previously found instances of this dependency.  These should be for lower versions.
							$DependencyList.Needed.Remove($_)
						}
						$DependencyList.Needed.Add($Dependency) > $null
						#We need to install this package, so we need to verify its dependencies as well
						Write-Debug "BuildDependencyList: Checking sub-package $($Dependency.Id)"
						$DependencyList = BuildDependencyList -RootPackage $PackageOnServer -DependencyList $DependencyList
					}
					default {
						throw "Unknown dependency status"
					}
				}
			} else {
				#dependency has already been processed in another iteration.
				Write-Debug "BuildDependencyList: $($Dependency.Id) has already been checked"
			}
		}
	} else {
		#else this root package has no dependencies
		Write-Debug "BuildDependencyList: $($RootPackage.Id) has no dependencies"
	}
	return $DependencyList
}